package com.study.doc.search;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.val;
import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;
import org.springframework.stereotype.Component;

/**
 * 构建正排索引和倒排索引
 */

public class Index {

    //jackson包下的类，用于序列化和反序列化
    ObjectMapper objectMapper = new ObjectMapper();

    //索引结构保存位置
    private String  INDEX_PATH = "D:/JAVA/jdk-8u311-docs-all/";

    //正排索引,下标就是docId
    private List<DocInfo> forwardIndex = new ArrayList<>();

    //倒排索引
    private Map<String,List<Weight>> invertedIndex = new HashMap<>();

    //一、通过正排索引获取文档
    public DocInfo getDoc(int docId){
        return forwardIndex.get(docId);
    }

    //二、通过倒排索引获取一个List
    public List<Weight> getWeights(String key){
        return invertedIndex.get(key);
    }

    //三、向索引里增加一个文档
    public void addDoc(String title, String url, String content){
        //构建正排索引
        DocInfo docInfo = buildForward(title,url,content);
        //构建倒排索引
        buildInverted(docInfo);
    }

    //构建倒排索引
    private void buildInverted(DocInfo docInfo){
        class WordCnt{
            public int titleCount;
            public int contentCount;
        }
        Map<String,WordCnt> wordCntMap = new HashMap<>();
        //1.对题目分词
        List<Term> terms = ToAnalysis.parse(docInfo.getTitle()).getTerms();
        //遍历分词结果并统计词语出现次数
        for (Term term : terms) {
            String word = term.getName();
            WordCnt wordCnt = wordCntMap.get(word);
            if (wordCnt == null){
                WordCnt newWordCnt = new WordCnt();
                newWordCnt.titleCount = 1;
                wordCntMap.put(word,newWordCnt);
            }else{
                //该词记录在案，计数器加一
                wordCnt.titleCount++;
            }
        }
        //2.对内容分词
        terms = ToAnalysis.parse(docInfo.getContent()).getTerms();
        for (Term term : terms) {
            String word = term.getName();
            WordCnt wordCnt = wordCntMap.get(word);
            if (wordCnt == null){
                WordCnt newWordCnt = new WordCnt();
                newWordCnt.contentCount = 1;
                wordCntMap.put(word,newWordCnt);
            }else{
                //该词记录在案，计数器加一
                wordCnt.contentCount++;
            }
        }
        //3.将分词结果汇总到倒排索引中
        for (Map.Entry<String,WordCnt> entry : wordCntMap.entrySet()){
            //计算该词在本文中的权重
            Weight weight = new Weight();
            weight.setDocId(docInfo.getDocId());
            weight.setWeight(entry.getValue().titleCount*10+entry.getValue().contentCount);

            List<Weight> invertedList =  invertedIndex.get(entry.getKey());

            if (invertedList == null){
                //该词还没有在其他文章中出现过，要建一个List把该词在本文中所占权重放进去
                List<Weight> newInvertedList = new ArrayList<>();
                newInvertedList.add(weight);
                invertedIndex.put(entry.getKey(),newInvertedList);
            }else{
                invertedList.add(weight);
            }
        }

    }

    //构建正排索引
    private DocInfo buildForward(String title, String url, String content){
        DocInfo docInfo = new DocInfo();
        docInfo.setContent(content);
        docInfo.setTitle(title);
        docInfo.setUrl(url);
        docInfo.setDocId(forwardIndex.size());
        forwardIndex.add(docInfo);
        return docInfo;
    }

    //四、将内存中的索引结构写入磁盘
    public void save(){
        //放入索引的目录路径
        File indexPath = new File(INDEX_PATH);
        //判断该目录是否存在，不存在就创建
        if (!indexPath.exists()){
            indexPath.mkdir();
        }
        //创建两个文件分别保存正排索引和倒排索引
        File forwardIndexFile = new File(INDEX_PATH+"forward.txt");
        File invertedIndexFile = new File(INDEX_PATH+"inverted.txt");
        try {
            //将索引结构写入本地
            objectMapper.writeValue(forwardIndexFile,forwardIndex);
            objectMapper.writeValue(invertedIndexFile,invertedIndex);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //五、将磁盘中的索引加载到内存中
    public void load(){
        //找到索引文件
        File forwardIndexFile = new File(INDEX_PATH+"forward.txt");
        File invertedIndexFile = new File(INDEX_PATH+"inverted.txt");
        try {
            //将索引文件中的内容加载到内存中
            forwardIndex = objectMapper.readValue(forwardIndexFile, new TypeReference<ArrayList<DocInfo>>(){});
            invertedIndex = objectMapper.readValue(invertedIndexFile, new TypeReference<Map<String, List<Weight>>>() {});
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        List<Term> list =  ToAnalysis.parse("我是闫鑫").getTerms();
        for (Term term : list) {
            System.out.println(term.getName());
        }
    }

}
